﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace Unity.Per.Common {

    public abstract class BaseModuleMono :MonoBehaviour, IBaseModule 
    {

        public string ModuleName { get; set; }
        public bool IsModuleInit { get; set; }
        public bool IsModuleStarted { get; set; }
        public bool IsModuleSuspend { get; set; }
        private bool IsDestoryGameObject;
        public IBaseModule FatherModule { get; set; }

        List<IBaseModule> subModuleList = new List<IBaseModule>();
        public bool IsMono { get =>true; }
        public BaseModulePriority Priority { get; set; }
        public bool IsEffectGameObject { get; set; }

        private void Awake() {
            DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] " + "GameObjectAwake ModuleInit", this);
            IsDestoryGameObject = false;
            ModuleInit();
        }
		
		private void OnEnable() { }
        private void Start() {}
        private void Update() {}
        private void LateUpdate() {}
        private void OnDisable() {}
		
        private void OnDestroy() {
            DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] " + "GameObjectDestory ModuleDestroy", this);
            IsDestoryGameObject = true;
            ModuleDestroy();
        }

        /// <summary>
        /// Module 初始化 ===================================
        /// </summary>
        public void ModuleInit() {
            if(IsModuleInit) {
                DebugMy.LogError("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] ModuleInit Had Invoke", this);
                return ;
            }


            IsModuleInit = true;
            IsModuleStarted = false;
            IsModuleSuspend = false;
            ModuleName = GetType().ToString();

            DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] " + "OnModuleInit", this);
            OnModuleInit();

        }

        /// <summary>
        /// OnModuleInit ===================================
        /// </summary>
        protected virtual void OnModuleInit() {
        }







        /// <summary>
        /// Module 启动 ===================================
        /// </summary>
        public void ModuleStart() {
            if(!IsModuleInit) {
                DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] Please Invoke ModuleInit First", this);
                return;
            }
            if(IsModuleStarted) {
                DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] ModuleStart Had Invoke", this);
                return;
            }
            
            IsModuleStarted = true;

            DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] " + "OnModuleStart", this);
            OnModuleStart();
        }

        /// <summary>
        /// OnModuleStart ===================================
        /// </summary>
        /// 
        protected virtual void OnModuleStart() {
        }






        /// <summary>
        /// Module Update ===================================
        /// </summary>
        public void ModuleUpdate() {

            if(IsModuleInit == false || IsModuleStarted == false || IsModuleSuspend == true) {
                //DebugMy.Log("Pleaase Invoke ModuleInit First", this);
                return;
            }


            lock ( subModuleList ) {
                foreach ( var Module in subModuleList ) {
                    Module.ModuleUpdate();
                }
            }
            //DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "OnModuleUpdate", this);
            OnModuleUpdate();
        }
        protected virtual void OnModuleUpdate() {

        }
        /// <summary>
        /// Module Update ===================================
        /// </summary>




        /// <summary>
        /// Module LateUpdate ===================================
        /// </summary>
        public void ModuleLateUpdate() {
            if(IsModuleInit == false || IsModuleStarted == false || IsModuleSuspend == true ) {
                //DebugMy.Log("Please Invoke ModuleInit First", this);
                return;
            }


            lock ( subModuleList ) {
                foreach ( var Module in subModuleList ) {
                    Module.ModuleLateUpdate();
                }
            }
            //DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "OnModuleLateUpdate", this);
            OnModuleLateUpdate();
        }
        protected virtual void OnModuleLateUpdate() {
        }
        /// <summary>
        /// Module LateUpdate ===================================
        /// </summary>






        /// <summary>
        /// ModuleEndOfFrame ===================================
        /// </summary>
        public void ModuleEndOfFrame() {
            if(IsModuleInit == false || IsModuleStarted == false || IsModuleSuspend == true ) {
                //DebugMy.Log("Please Invoke ModuleInit First", this);
                return;
            }

            lock ( subModuleList ) {
                foreach ( var Module in subModuleList ) {
                    Module.ModuleEndOfFrame();
                }
            }
            //DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "DoLifeFuncitonWaitForEndOfFrame", this);
            OnModuleEndOfFrame();
        }
        protected virtual void OnModuleEndOfFrame() {
        }
        /// <summary>
        /// ModuleEndOfFrame ===================================
        /// </summary>





        /// <summary>
        /// ModuleStop ===================================
        /// </summary>
        public void ModuleStop() {
            if(IsModuleInit == false) {
                DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] ModuleStop: Please Invoke ModuleInit First", this);
                return;
            }

            if ( IsModuleStarted == false ) {
                DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] ModuleStop: Please Invoke ModuleStart First", this);
                return;
            }

            if ( IsModuleSuspend ) {
                DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] ModuleStop: IsModuleSuspend == true ; So Invoke ModuleResume", this);
                ModuleResume();
            }
            IsModuleSuspend = false;

            lock ( subModuleList ) {
                foreach ( var Module in subModuleList ) {
                    Module.ModuleStop();
                }
            }
            DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] " + "OnModuleStop", this);
            OnModuleStop();

            IsModuleStarted = false;
        }
        protected virtual void OnModuleStop() {
        }
        /// <summary>
        /// ModuleStop ===================================
        /// </summary>




        /// <summary>
        /// ModuleDestroy ===================================
        /// </summary>
        public void ModuleDestroy() {

            if(IsModuleInit == false) {
                DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] ModuleDestroy: Please Invoke ModuleInit First", this);
                return;
            }
            IsModuleInit = false;

            if ( IsModuleSuspend ) {
                DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] ModuleDestroy: IsModuleSuspend == true ; So Invoke ModuleResume", this);
                ModuleResume();
            }
            IsModuleSuspend = false;

            if ( IsModuleStarted ) {
                DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] ModuleDestroy: IsModuleStarted == true ; So Invoke ModuleStop", this);
                ModuleStop();
            }
            IsModuleStarted = false;
            lock ( subModuleList ) {
                foreach ( var Module in subModuleList ) {
                    Module.ModuleDestroy();
                }
            }

            subModuleList.Clear();
            DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] " + "OnModuleDestroy", this);
            OnModuleDestroy();


            if ( gameObject && IsDestoryGameObject==false) {
                DestroyImmediate(gameObject);
            }
            ModuleName = "";
            FatherModule = null;

        }
        protected virtual void OnModuleDestroy() {

        }
        /// <summary>
        /// ModuleDestroy ===================================
        /// </summary>



        /// <summary>
        /// ModuleResume ===================================
        /// </summary>
        public void ModuleResume()
        {

            if ( IsModuleSuspend == false)
            {
                DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] ModuleResume: Please Invoke ModuleSuspend First", this);
                return;
            }

            lock ( subModuleList ) {
                foreach ( var Module in subModuleList ) {
                    Module.ModuleResume();
                }
            }
            DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] " + "OnModuleResume", this);
            OnModuleResume();

            IsModuleSuspend = false;

        }
        protected virtual void OnModuleResume()
        {

        }



        /// <summary>
        /// ModuleSuspend ===================================
        /// </summary>
        public void ModuleSuspend()
        {

            if ( IsModuleStarted == false)
            {
                DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] ModuleSuspend: Please Invoke ModuleStart First", this);
                return;
            }

            lock ( subModuleList ) {
                foreach ( var Module in subModuleList ) {
                    Module.ModuleSuspend();
                }
            }
            DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + GetType() + "' [HashCode: " + GetHashCode() + "] " + "OnModuleSuspend", this);
            OnModuleSuspend();

            IsModuleSuspend = true;

        }
        protected virtual void OnModuleSuspend()
        {

        }










        public void AddModule(IBaseModule Module, bool isEffectGameObject = true, BaseModulePriority priority = BaseModulePriority.Middle) {
            if(Module == null)
                return;

            Module.FatherModule = this;

            DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + Module.GetType() + "' [HashCode: " + Module.GetHashCode() + "] Add To Module '" + GetType(), this);
            lock(subModuleList) {
                if (subModuleList.Contains(Module) == false) {
                    subModuleList.Add(Module);
                }
            }
        }

        public void RemoveModule(IBaseModule Module) {
            if(Module == null)
                return;

            DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + Module.GetType() + "' [HashCode: " + Module.GetHashCode() + "] Remove From Module '" + GetType(), this);
            lock(subModuleList) {
                if (subModuleList.Contains(Module) == true) {
                    subModuleList.Remove(Module);
                }
            }

            Module.ModuleDestroy();
            Module.FatherModule = null;
        }

        public void RemoveAllModule() {
            lock ( subModuleList ) {
                foreach ( var Module in subModuleList ) {
                    DebugMy.Log("[HashCode: " + GetHashCode() + "] " + "Module '" + Module.GetType() + "' [HashCode: " + Module.GetHashCode() + "] Remove All Module '" + GetType(), this);
                    subModuleList.Remove(Module);
                    Module.ModuleDestroy();
                }
            }
            subModuleList.Clear();
        }

        public virtual T GetSubModule<T>() where T : IBaseModule {

            lock(subModuleList) {
                foreach(var Module in subModuleList) {
                    if(typeof(T).ToString() == Module.ModuleName) {
                        DebugMy.Log("[HashCode: " + GetHashCode() + "] Module" + typeof(T).ToString() + " Found ", this);
                        return (T)Module;
                    }
                }
            }
            DebugMy.Log("[HashCode: " + GetHashCode() + "] Module" + typeof(T).ToString() + " No Found ", this);
            return default(T);
        }

        /// <summary>
        /// 返回直接父
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public virtual T GetFatherModule<T>() where T : IBaseModule {
            IBaseModule father = FatherModule;

            while(father != null) {
                if(father.ModuleName == typeof(T).ToString()) {
                    DebugMy.Log("[HashCode: " + GetHashCode() + "] FatherModule" + father.ModuleName + " Found ", this);
                    return (T)father;
                }
                father = father.FatherModule;
            }
            DebugMy.Log("[HashCode: " + GetHashCode() + "] FatherModule" + typeof(T).ToString() + " Found ", this);
            return default(T);
        }


        public T Transition<T>(IBaseModule data) where T : class {
            if((data as T) == null)
                throw new ArgumentException(String.Format("Invalid type: {0} passed to event expecting {1}", data.GetType(), typeof(T)));
            return data as T;
        }

    }
}
